package pl.enigmatic.parser.string;

import java.lang.reflect.Array;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;

import pl.enigmatic.exception.WrongArgumentsException;
import pl.enigmatic.util.Reflection;


// TODO może umożliwić konwersję napisów postaci [[1,2,3],[3,2,1]]
public class ArrayConverter<T> extends Converter<T[]> {

	static {
		DOMConfigurator.configure("log4j.xml");
	}

	private static final Logger logger = Logger.getLogger(ArrayConverter.class);

	public static final int MAX_DEPTH = 3;

	public final static Pattern SPACE_PATTERN = Pattern.compile(" +");
	public final static String SPACE_SEPARATOR = " ";
	public final static Pattern COMMA_PATTERN = Pattern.compile(" *, *");
	public final static String COMMA_SEPARATOR = ",";
	public final static Pattern LINE_PATTERN = Pattern.compile("\\n+");
	public final static String LINE_SEPARATOR = "\n";

	protected final Class<T> clazz;
	protected final Converter<T> converter;
	private final Pattern pattern;
	private final String separator;

	// FIXME konstruktory mogą nie działać jak trzeba!!!
	public ArrayConverter(final Class<T> clazz, final Converter<T> converter, final Pattern pattern, final String separator) {
		this.clazz = clazz;
		this.converter = converter;
		this.pattern = pattern;
		this.separator = separator;
	}

	public ArrayConverter(final Class<T> clazz, final Pattern pattern, final String separator) {
		this(clazz, Converter.createDefaultConverter(clazz), pattern, separator);
	}

	@SuppressWarnings("unchecked")
	public ArrayConverter(final Class<T[]> arrayClazz, final Converter<T> simpleTypeConverter) {
		clazz = (Class<T>) arrayClazz.getComponentType();
		converter = simpleTypeConverter;
		switch (Reflection.arrayDepth(arrayClazz)) {
			case 1:
				this.pattern = SPACE_PATTERN;
				this.separator = SPACE_SEPARATOR;
			break;
			case 2:
				this.pattern = COMMA_PATTERN;
				this.separator = COMMA_SEPARATOR;
			break;
			case 3:
				this.pattern = LINE_PATTERN;
				this.separator = LINE_SEPARATOR;
			break;
			default:
				this.pattern = null;
				this.separator = null;
				logger.error(String.format("You should not create converter %1$s for class %2$s.", ArrayConverter.class.getName(), Reflection
						.arrayBase(arrayClazz).getPackage().getName()
						+ "." + arrayClazz.getSimpleName()), new WrongArgumentsException(
						"type passed should represent an array of dimension 1 or 2 only"));
			break;
		}
	}

	@SuppressWarnings("unchecked")
	public ArrayConverter(final Class<T[]> arrayClazz) {
		this(arrayClazz, Converter.createDefaultConverter((Class<T>) arrayClazz.getComponentType()));
	}

	@SuppressWarnings("unchecked")
	@Override
	public T[] convert(String value) {
		if (value == null) { return null; }
		value = pattern.matcher(value).replaceAll(separator).trim();
		if (value.equals("")) { return (T[]) Array.newInstance(clazz, 0); }
		final String[] split = value.trim().split(separator);
		final T[] res = (T[]) Array.newInstance(clazz, split.length);
		for (int i = 0; i < split.length; i++) {
			res[i] = converter.convert(split[i]);
		}
		return res;
	}

	@Override
	public String convertBack(final T[] value) {
		if (value == null) {
			return null;
		} else if (value.length == 0) return "";
		String res = "";
		for (final T element : value) {
			res += converter.convertBack(element) + separator;
		}
		return res.substring(0, res.length() - separator.length());
	}

}